//
//  DeviceProvisionRegisterable.swift
//  ZhiTing
//
//  Created by iMac on 2022/7/21.
//

import Foundation
import Combine

/// 设备注册步骤状态  失败时从失败的步骤开始重试
enum RegisterDeviceState {
    /// 初始状态
    case origin
    /// 获取设备AccessToken
    case getDeviceAccessToken
    /// 获取设备接入MQTT密码
    case getDeviceMqttPassword(accessToken: String)
    /// 将设备连接服务器
    case connectDeviceToServer(accessToken: String, pwd: String? = nil)
    /// 添加设备
    case connectDevice
}

/// 设备配网成功后 设备注册遵循的通用协议
protocol DeviceProvisionRegisterable: AnyObject {
    /// 用于局域网扫描设备
    var udpDeviceTool: UDPDeviceTool? { get set }
    
    /// 当前注册设备步骤状态
    var currentRegisterDeviceState: RegisterDeviceState { get set }
    
    /// 硬件设备ID
    var deviceID: String { get set }
    
    /// 搜索发现的设备(用于配网成功后 调用添加设备接口)
    var discoverDeviceModel: DiscoverDeviceModel? { get set }

    /// 注册设备回调 (注册设备结果, 错误信息, 错误码, 设备id, 控制页路径)
    var registerDeviceCallback: ((_ success: Bool, _ error: String?, _ error_code: Int?, _ device_id: Int?, _ control: String?) -> Void)? { get set }
    
    /// 注册设备
    func registerDevice()
    /// 添加配网成功后的设备
    func addDevice()
    /// 获取设备accessToken
    func getDeviceAccessToken(deviceID: String)
    /// 获取设备mqtt密码
    func getDeviceMqttPassword(accessToken: String)
    /// 将设备连接服务器
    /// - Parameter deviceID: 设备id
    /// - Parameter accessToken: 设备accessToken
    /// - Parameter pwd: 设备连接密码
    func connectDeviceToServer(deviceID: String, accessToken: String, pwd: String?)
}

extension DeviceProvisionRegisterable {
    /// 注册设备
    func registerDevice() {
        ZTConsole.log("注册设备 步骤:\(currentRegisterDeviceState)")
        
        switch currentRegisterDeviceState {
        case .origin:
            if AuthManager.shared.currentArea.id != nil
                && !AuthManager.shared.currentArea.is_bind_sa
                && UserManager.shared.isLogin { // 云端虚拟SA家庭 配网后局域网搜索到设备,先注册到云端再添加设备
                getDeviceAccessToken(deviceID: deviceID)
            } else if AuthManager.shared.currentArea.id != nil
                        && AuthManager.shared.currentArea.is_bind_sa { // 真实SA家庭 配网后添加设备
                discoverDeviceModel?.iid = deviceID
                addDevice()
            } else {
                registerDeviceCallback?(false, "注册设备失败", 0003, nil, nil)
            }
        case .getDeviceAccessToken:
            getDeviceAccessToken(deviceID: deviceID)
        case .getDeviceMqttPassword(let accessToken):
            getDeviceMqttPassword(accessToken: accessToken)
        case .connectDeviceToServer(let accessToken, let pwd):
            connectDeviceToServer(deviceID: deviceID, accessToken: accessToken, pwd: pwd)
        case .connectDevice:
            addDevice()
        }
    }

    /// 添加配网成功后的设备
    func addDevice() {
        currentRegisterDeviceState = .connectDevice
        
        guard let discoverDeviceModel = discoverDeviceModel else {
            ZTConsole.log("未获取到配网设备信息")
            registerDeviceCallback?(false, "未获取到配网设备信息", 0004, nil, nil)
            return
        }
        
        var cancellable: AnyCancellable?
        
        /// 添加websocket发现的设备结果回调
        cancellable = AppDelegate.shared.appDependency.websocket.connectDevicePublisher
            .receive(on: DispatchQueue.main)
            .timeout(.seconds(30), scheduler: DispatchQueue.main)
            .filter { $0.0 == self.deviceID }
            .sink(receiveCompletion: { [weak self] _ in
                guard let self = self else { return }
                ZTConsole.log("添加设备超时无响应")
                self.registerDeviceCallback?(false, "添加设备超时无响应", 0005, nil, nil)
                cancellable?.cancel()
            }, receiveValue: { [weak self] (iid, response) in
                guard let self = self else { return }
                if response.success {
                    if let id = response.data?.device?.id, let control = response.data?.device?.control {
                        ZTConsole.log("添加设备成功")
                        self.registerDeviceCallback?(true, nil, nil, id, control)
                    } else {
                        ZTConsole.log("添加设备失败:\(response.error?.message ?? "")")
                        self.registerDeviceCallback?(false, "添加设备失败:\(response.error?.message ?? "")", response.error?.code, nil, nil)
                    }
                } else {
                    ZTConsole.log("添加设备失败:\(response.error?.message ?? "")")
                    self.registerDeviceCallback?(false, "添加设备失败:\(response.error?.message ?? "")", response.error?.code, nil, nil)
                }
                cancellable?.cancel()
            })
            
        
        AppDelegate.shared.appDependency.websocket.executeOperation(operation: .connectDevice(domain: discoverDeviceModel.plugin_id, iid: deviceID ))
    }
    

    /// 获取设备accessToken
    func getDeviceAccessToken(deviceID: String) {
        currentRegisterDeviceState = .getDeviceAccessToken
        ApiServiceManager.shared.getDeviceAccessToken { [weak self] resp in
            guard let self = self else { return }
            if self.discoverDeviceModel?.protocol == "mqtt" { /// 如果设备接入mqtt时 需先获取设备接入mqtt密码
                self.getDeviceMqttPassword(accessToken: resp.access_token)
            } else {
                self.connectDeviceToServer(deviceID: deviceID, accessToken: resp.access_token)
            }
            
        } failureCallback: { [weak self] code, err in
            guard let self = self else { return }
            self.registerDeviceCallback?(false, "获取设备acessToken失败", code, nil, nil)
            ZTConsole.log(err)
        }

    }
    
    /// 获取设备mqtt密码
    func getDeviceMqttPassword(accessToken: String) {
        currentRegisterDeviceState = .getDeviceMqttPassword(accessToken: accessToken)
        ApiServiceManager.shared.getDeviceMqttPassword(device_id: deviceID, model: self.discoverDeviceModel?.model ?? "") { [weak self] mqttResp in
            guard let self = self else { return }
            self.connectDeviceToServer(deviceID: self.deviceID, accessToken: accessToken, pwd: mqttResp.password)
        } failureCallback: { [weak self] code, err in
            guard let self = self else { return }
            self.registerDeviceCallback?(false, "获取设备接入mqtt密码失败", code, nil, nil)
        }

    }


    
    /// 将设备连接服务器
    /// - Parameter deviceID: 设备id
    /// - Parameter accessToken: 设备accessToken
    /// - Parameter pwd: 设备连接密码
    func connectDeviceToServer(deviceID: String, accessToken: String, pwd: String? = nil) {
        currentRegisterDeviceState = .connectDeviceToServer(accessToken: accessToken, pwd: pwd)
        
        UDPDeviceTool.stopUpdateAreaSAAddress()
        udpDeviceTool = UDPDeviceTool()
        
        guard let areaId = AuthManager.shared.currentArea.id,
              AuthManager.shared.currentArea.is_bind_sa == false
        else {
            ZTConsole.log("家庭id不存在或家庭已有SA，无需设置设备服务器")
            registerDeviceCallback?(false, "家庭id不存在", 0006, nil, nil)
            return
        }

        var searchCancellable: AnyCancellable?

        /// 局域网内搜索到设备
        searchCancellable = udpDeviceTool?.deviceSearchedPubliser
            .receive(on: DispatchQueue.main)
            .timeout(.seconds(30), scheduler: DispatchQueue.main)
            .sink(receiveCompletion: { [weak self] _ in
                guard let self = self else { return }
                ZTConsole.log("局域网内未发现到设备")
                self.registerDeviceCallback?(false, "局域网内未发现到设备", 0007, nil, nil)
                searchCancellable?.cancel()
            }, receiveValue: { [weak self] device in
                guard let self = self else { return }
                self.discoverDeviceModel?.iid = device.id
                self.udpDeviceTool?.connectDeviceToSC(device: device, areaId: areaId, accessToken: accessToken, protocol: self.discoverDeviceModel?.protocol, pwd: pwd)
                searchCancellable?.cancel()
            })
            
        
        var setServerCancellable: AnyCancellable?
        
        /// 设备连接服务器
        setServerCancellable = udpDeviceTool?.deviceSetServerPublisher
            .receive(on: DispatchQueue.main)
            .timeout(.seconds(30), scheduler: DispatchQueue.main)
            .sink(receiveCompletion: { [weak self] _ in
                guard let self = self else { return }
                ZTConsole.log("设置设备连接服务器超时")
                self.registerDeviceCallback?(false, "设置设备连接服务器超时", 0008, nil, nil)
                setServerCancellable?.cancel()
            }, receiveValue: { [weak self] success in
                guard let self = self else { return }
                if success {
                    self.addDevice()
                    setServerCancellable?.cancel()
                } else {
                    self.registerDeviceCallback?(false, "设备连接服务器失败", 0008, nil, nil)
                    setServerCancellable?.cancel()
                }

                
            })
            
        
        try? udpDeviceTool?.beginScan(notifyDeviceID: deviceID)
        

    }
    
}
